Bean加载的总体流程
以这行代码为起点,探究这背后到底经历了什么?
1 | MyTestBean bean=(MyTestBean)bf.getBean("MyTestBean"); |
- 调用
getBean("MyTestBean")背后实际是调用了doGetBean(name,null,null,false)doGetBean方法。这个方法干的事情有些多,但总结起来就这们几步:
- 通过传入的的name参数,提取beanName属性.这个地方有可能会有疑问,传入的name不就是bean的Name吗。其实不然,因为spring还允许有别名。
- 尝试使用直接从缓存获取,或者sinletionFactories中的ObjectFactory中获取,具体的也就是在
Object sharedInstance = getSingleton(beanName);代码中。这里的缓存其实就是指DefaultSingletonBeanRegistry类中的singletonObjects:Map。这个缓存就是一个ConcurrentHashMap其中键为BeanName,值就是对应的Bean。 具体的代码实现如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38/**
*这个方法中蕴含了解决循环依赖的原理
*/
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//尝试从缓存中拿该bean
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
//如果缓存中没有该bean,且该bean正在创建中
synchronized (this.singletonObjects) {
/**尝试从提前暴露缓存中拿到该bean
*这个earlySingletonObjects也是一个map
*/
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
/**
* 如果从提前暴露缓存中拿也没拿到,且是允许提前引用的
* 就尝试从singletonFactories中获取
*/
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
/**
* 如果获取到了该bean的单例工厂,就从单例工厂中来获取
* 该bean
*/
singletonObject = singletonFactory.getObject();
/**
* 将获取到的对象加入,提前暴露缓存,这个时候,其实还不能算作bean
* 因为该对象还没有走完bean的生命周期
*/
this.earlySingletonObjects.put(beanName, singletonObject);
//从单例工厂缓存中移除该bean的单例工厂
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
回到刚才的doGetBean方法,通过getSingleton方法的一番努力,如果拿到了bean,现在我们可以返回实例了。
这一步是由bean = getObjectForBeanInstance(sharedInstance, name, beanName, null)来实现
但值得注意的是,有些存在beanFactory的时候,spring并不是返回的实例本身,而是返回指定方法返回的实例。这个地方有点绕,其实很好理解,因为spring创建bean本身就有两种创建方法(大分类),一种是利用构造器,一种是利用工厂方法(又可分为静态工厂和实例工厂)。如果是利用工厂方法,我们当然不能直接返回工厂对象咯,而是应该返回该工厂对象中getObejct()方法返回的对象。
- 如果没有拿到bean,那
doGetBean还要经过一番努力。如果已经加载的类中不包括beanName(已经加载的类存在beanDefinitionMap中,值得注意的BeanDefintion对应Bean的关系,就像Class对应Object的关系一样)。那么就尝试从parentBeanFactory中获取BeanFactory,如果没找到就递归到BeanFactory中去找。 如果不仅仅做类型检测而是需要创建bean,就将该beanName记录为正在创建的状态。这个一步由这段代码实现:
1
2
3if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}进行属性的准备,如果是子bean的话,需要合并父类的相关属性,这个也很好理解,如果一个类有继承的父类的时候,它本身包含的属性是不完整的,它还有一部分属性在它的父类中体现。这一步主要由这段代码实现:
1
2final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);如果存在依赖,这需要递归实例化依赖的bean。这里涉及一个不太常见的用法
depend-on。这一步主要由这段代码实现:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}创建了bean之后,就可以实例化mbd本身了。这个时候,又分为了两种情况,一种是单例的创建,一种是原型的创建。
- 类型的转换
创建Bean
createBean函数负责创建bean。这个函数在doGetBean方法中调用过。现在进入这个方法内部,查看其具体实现。
首先这个方法的声明是这样的:
1 | protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) |
它主要干了这些事情:
- 调用
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);,根据设置的class属性和className来解析Class。这个方法的内部没有什么特别的。并且利用该方法得到的resolvedClass来初始化mbdToUse:RootBeanDefinition - 验证准备覆盖的方法
mbdToUse.prepareMethodOverrides();.说人话,就是对override属性进行标记及验证。 - 通过调用
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);给bean后置处理器一个通过代理类替换目标类的机会。这个地方需要注意。 - 调用
Object beanInstance = doCreateBean(beanName, mbdToUse, args);来进行Bean的创建
循环依赖
什么是循环依赖?
循环依赖就是循环引用。就是两个或多个bean相互之间持有对方。比如beanA中引用了beanB,而beanB中引用了beanA。那么这两个bean之间就形成了循环依赖。值得注意的是循环依赖不等于循环调用。循环调用是方法调用之间成环。循环调用是无解的。
spring是如何解决循环依赖的?
要回答回答这个问题,首先要清楚,spring中bean有哪几种循环依赖,哪些循环依赖是可以解决的。
循环依赖的分类
- 构造器循环依赖
这种依赖的成因是因为构造器注入形成的循环依赖。这种依赖是无解的。因此,我们不太建议使用构造器注入。至于为什么无法解决构造器循环依赖,原因其实非常的简单。举例:假如beanA在调用构造器的时候,发现需要beanB作为参数,然后去调用beanB的构造器,发现又需要beanA作为参数,这种情况根本没得搞,死循环了。 - setter循环依赖
表示通过setter注入方式构成的循环依赖。setter循环依赖在单例作用域下是可以解决的。spring通过提前暴露完成实例化的bean来解决setter循环依赖。举个栗子:beanA实例化后,进行属性赋值的时候,发现它需要通过setter注入beanB,然后他就去实例化beanB,beanB实例化之后,在beanB属性赋值的时候,发现需要注入beanA,这个时候因为beanA已经提前暴露了(它现在还没完整的创建),beanB发现了beanA,然后直接把beanA注入即可,这个时候beanB的属性赋值阶段,已经完成了,这个时候我们再看beanA,这个时候直接把beanB注入beanA即可了,这样循环依赖就解决了。
在解决循环依赖的过程中,很关键的两点是:哪些bean正在创建需要透明;bean实例化之后,就可暴露出来,供其它bean注入。
- prototype范围的依赖处理
prototype范围的循环依赖也是无解的,因为spring不会缓存prototype范围的对象,因此无法实现提前暴露。
常规的bean的创建
总体的流程
所谓的常规的,就是指在createBean中调用了Object bean = resolveBeforeInstantiation(beanName, mbdToUse);后,并没有创建代理,就调用doCreateBean。该方法进行的就是常规的bean的创建。
这个类主要做了这些事情:
如果是单例则首先清除缓存
1
2
3if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}根据bean所指定的策略创建实例,将beanDefinition转换为BeanWrapper,这个方法的内部实现其实比较复杂。如果存在工厂方法,就使用工厂方法进行初始化;如果有多个构造器,则根据参数匹配构造器来初始化;如果没有工厂方法,也不存在带参数的构造器,则使用默认构造方法进行实例化。
1
instanceWrapper = createBeanInstance(beanName, mbd, args);
判断是否需要提前曝光,根据这些条件来进行判断:单例&允许循环依赖&当前bean正在创建过程中。当需要提前曝光时,在bean创建完成之前将创建实例的ObjectFactory加入工厂
1
2
3
4
5
6
7
8
9
10
11
12
13boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
/**
* 为避免后期循环依赖,可以在bean初始化完成之前将创建实例的ObjectFactory加入工厂
*getEarlyBeanReference需要注意,AOP就是在这将advice织入bean的
*/
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}属性填充,也就是在这个地方,就可能出现循环依赖,如果存在依赖其它bean的属性,则会递归初始化bean
1
populateBean(beanName, mbd, instanceWrapper);
调用初始化方法
1
exposedObject = initializeBean(beanName, exposedObject, mbd);
检测依赖
1
2
3
4
5removeSingletonIfCreatedForTypeCheckOnly(dependentBean)for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
removeSingletonIfCreatedForTypeCheckOnly(dependentBean)方法所做的事情就是去一个alreadyCreated:Map中看,该bean所依赖的bean,是否已经被创建了,如果被创建了,就将该bean从三级缓存和registeredSingletons:map中删除,并返回true。如果所依赖的bean已经被创建,就将该bean加入actualDependentBeans:Set.完成对所有的依赖的bean的检测之后,如果actualDependentBeans:Set不为空,就抛出循环依赖异常(这个地方值得注意的是,如果一个bean创建成功了,那么它所依赖的bean也一定是创建了的,如果它所依赖的bean没有被创建,这就说明存在循环依赖)
根据scope注册bean
所谓的注册就是将bean加入一个disposableBeans:map中去。便于在销毁时使用。1
registerDisposableBeanIfNecessary(beanName, bean, mbd);
完成创建并返回
完成bean实例化的createBeanInstance方法
instanceWrapper = createBeanInstance(beanName, mbd, args);这个方法主要完成了bean的实例化。这也是bean生命周期的第一步。
这个方法主要做以下几件事情:
- 拿到beanName对应的class
1
Class<?> beanClass = resolveBeanClass(mbd, beanName);
像class这些信息在spring初始化的使用就已经拿到了。这个地方只是取出来就可以了。
- 如果有相应的工厂方法,就使用工厂方法创建bean
1
2
3if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
spring创建bean无非就是两种方案,一个是利用工厂方法,一个是利用构造器。如果没有对应的工厂方法,那就只好使用构造器来实例化。
- 没有对应的工厂方法,就使用构造器来实例化。先锁定构造方法
1
2
3
4
5
6synchronized (mbd.constructorArgumentLock) {
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
因为一个类可能有多个构造方法,所以这个地方需要根据参数来确定调用哪个构造函数或工厂方法。因为这个操作是比较费时的所以spring采用了缓存的机制。
经过解析,调用相应的构造方法进行初始化
1
2
3
4
5
6
7
8
9
10
11//如果已经解析过勒,则使用解析好的构造函数方法,而无需再次锁定
if (resolved) {
if (autowireNecessary) {
//构造函数自动注入
return autowireConstructor(beanName, mbd, null, null);
}
else {
//使用默认构造函数注入
return instantiateBean(beanName, mbd);
}
}如果需要根据参数来解析构造函数,就执行以下几步
1
2
3
4
5
6// Candidate constructors for autowiring?
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
带参数的实例化,其实非常的复杂,因为它充满了不确定性。
容器功能的拓展
ApplicationContext对BeanFactory进行了拓展。ApplicationContext包含了BeanFactory的所有功能。
从这行代码,为起点来看它背后的东西:
1 | ApplicationContext bf=new ClassPathXmlApplicationContext("beanFactory.xml"); |
- 首先它调用了一个有参构造器
1
2
3public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
它又调用
1 | public ClassPathXmlApplicationContext( |
- 设置了配置文件的路径,之后就可以进行配置文件的解析和各种功能的实现了。这些东西都是在
refresh方法中实现了。
完整的代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
//准备刷新的上下文环境
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
//初始化BeanFactory并进行xml文件的读取
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
//对beanFactory进行功能上的填充
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
//子类覆盖方法做额外的处理
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
//调用各种beanFactory处理器
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
//注册拦截bean创建的bean处理器
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
//为上下文初始化message源,即不同的语言的消息体,国际化处理
initMessageSource();
// Initialize event multicaster for this context.
//初始化应用消息广播器
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
//留给子类来初始化其它的bean
onRefresh();
// Check for listener beans and register them.
//在所有的bean中查找listener bean,并注册到消息广播器中
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
//初始化剩下的非懒加载的单实例
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
//完成刷新过程
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
它主要做了这些事情:
- 初始化前的准备工作。例如对系统属性或者环境变量进行准备和验证
初始化beanFactory并进行xml文件的读取。因为ApplicationContext包含了BeanFactory所提供的一切特征,在这一步就是利用BeanFactroy中的xml文件读取的相关功能。也就是从这一步开始ApplicationContext已经包含了一个BeanFactory了。这段代码可以佐证:
1
2
3
4
5
6
7
8
9protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
//这一步就创建了一个BeanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}对BeanFactory进行各种功能填充,常见的
@Autowired就是在这一步增加支持的。- 子类覆盖方法做额外的处理。通过这个拓展点,可以方便程序员来拓展spring的功能
- 调用各种beanFactoty处理器
- 注册拦截bean创建的bean处理器,这一步并没有调用,真正调用的是在getBean的时候
- 为上下文初始化message源,对不同语言的消息题进行国际化处理。
- 初始化应用消息广播器。
- 留给子类来初始化其它的bean
- 在所有的注册的bean中查找 listener bean,注册到消息广播器中。
- 初始化涉嫌的单实例
- 完成刷新过程
重要步骤细节分析
1. 环境准备
环境准备调用的是protected void prepareRefresh()方法,该方法的内部实现,有行关键的代码:
1 | //留给子类覆盖的,这个方法内部是空的 |
其实这两行代码基本没做什么实际的事情,包括第二行代码,因为实际上因为没有什么需要验证的属性,所以第二行代码也没有做什么实际的事情。这两行的代码的意义在于,提供了拓展点。通过重写initPropertySources()来实现个性化的属性处理,之后再利用validateRequiredProperties对属性进行验证。
2. 加载BeanFactory
获取BeanFactory,这个工作由这行代码实现:
1 | ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); |
也正是从这个地方开始,ApplicationContext有了BeanFactory的全部功能。obtainFreshBeanFactory()方法中调用了refreshBeanFactory()方法,由这个方法来完成了BeanFactory的初始化工作。
这个方法完成的首要工作就是创建beanFactory:DefaultListableBeanFactory,这个对象非常的重要,它整个IOC容器的关键。之后是指定序列化ID,定制BeanFactory,加载BeanDefinition,最后使用全局变量记录BeanFactory实例。
3. 加载beanDefinition
加载beanDefeinition的第一步就是初始化用于读取配置文件的beanDefinitionReader:XmlBeanDefinitionReader.创建成功之后,对beanDefinitionReader做环境变量的设置之后,就可以使用loadBeanDefinitions(beanDefinitionReader)读取并解析spring配置文件,来加载BeanDefinitinon。
4. 功能拓展
spring在prepareBeanFactory(ConfigurableListableBeanFactory beanFactory),方法中完成了ApplicationContext的功能的拓展。
主要是添加了以下几个方面的拓展:
- 增加对spEL语言的支持
- 证件对属性编辑器的支持
- 增加对一些内置类的信息注入。
- 设置了依赖功能可能忽略的接口
- 注册了一些固定依赖的属性
- 增加了AspectJ的支持
- 将相关环境变量及属性注册以单例模式注册
5. BeanFactory的后处理
BeanFactory作为spring中容器功能的基础,用于存放所有已经加载的bean,为了保证程序的高可拓展性,Spring针对BeanFactory做了大量的拓展。PostProcessor等都是在这个地方实现的。
后处理主要做了这些事情:
- 激活BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);调用了所有的后置处理器,其中首先调用所有的硬编码配置的后置处理器,其后调用了配置注册的后置处理器。在执行的时候,它会按照配置中order属性来按一定的顺序来执行那些后置处理器。spring通过三个list来记录这些后置处理器。它们分别是: registryPostProcessors记录通过硬编码方式注册的BeanDefinitionRegistryPostProcessor类型的处理器regularPostProcessors记录通过硬编码方式注册的BeanFactoryPostProcessor类型的处理器registryPostProcessorBeans记录通过配置方式注册的BeanDefinitonRegistryPostProcessor类型的处理器。
6. 注册BeanPostProcessor
实现了对BeanFactoryPostProcessor的调用之后,就要进行BeanPostProcessor的注册了。spring中许多的功能都是通过后处理器的方式来拓展的。BeanPostProcessor的处理与BeanFactoryPostProcessor的处理几位相似。在注册之前需要根据 PriorityOrdered进行排序,根据Ordered进行排序或者无序。
7. 初始化消息资源
初始化消息资源是为了进行国际化处理。在确定特定类型的本地化信息的时候,,需要两个条件,一个是“语言条件”,另一个是“国家/地区的类型”。spring定义了国际化信息的MessageSource接口。
8. 初始化ApplicationEventMulticaster
首先来展示一下Spring事件监听器的简单用法:
1.定义监听事件
1 | public classs TestEvent extends ApplicationEvent{ |
定义监听器
1
2
3
4
5
6
7
8public class TestListener implements ApplicationListener{
public void onApplicationEvent(ApplicationEvent event){
if(event instanceof TestEvent){
TestEvent testEvent=(TestEvent)event;
testEvent.print();
}
}
}编写配置文件
1
<bean id="testListener" class="com.test.event.TestListener"/>
测试
1
2
3
4
5
6
7public class Test{
public static void main(String[] args){
ApplicationContext context=new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
TestEvent event=new TestEvent("hello","msg");
context.publishEvent(event);
}
}
控制台输出:
1 | msg |
具体的操作由initApplicationEventMulticaster();来实现,它无非就处理了两种情况一种是:如果用户自定义了事件广播器,那么使用用户自定义的事件广播器。第二种情况:如果用户没有自定义事件广播器,那么使用默认的ApllicationEventMulticaster。
这个地方运用了设计模式的观察者模式.
当spring事件产生的时候会默认使用SimpleApplicationEventMulticaster的multicastEvent来广播事件,Spring会遍历所有的监听器,并使用监听器的onApplicationEvent方法来进行监听器的处理。因此其实每个监听器都可以收到事件,是否进行处理则由监听器来决定。
9. 注册监听器
注册监听器由registerListeners();
1 | protected void registerListeners() { |
10. 初始化非延时加载的单例
其中包括ConversionService的配置,配置冻结以及非延迟加载的bean的初始化工作。所谓的冻结配置就是注册的bean的定义将不被修改或进行任何进一步的处理。ApplicationContext实现的默认行为就是在启动时将所有单例bean提前进行实例化。
v1.5.2